package test.seqaligner;

import jaligner.Alignment;
import jaligner.Sequence;
import jaligner.SmithWatermanGotoh;
import jaligner.matrix.Matrix;
import jaligner.matrix.MatrixLoader;
import jaligner.util.SequenceParser;

import java.io.File;

public class SequenceAlignmentRunner{
	/*
	 * Controla la granularidad de la tarea recursiva (llamado a "doPerform")
	 * Si la cantidad de entradas de la BD de secuencias a chequear de una tarea 
	 * con respecto al total de la BD es mayor a THRESHOLD, se divide el procesamiento
	 * en dos partes, de lo contrario se hace de forma secuencial.
	 */
	static float THRESHOLD = 0.10f;


	public static float getTHRESHOLD() {
		return THRESHOLD;
	}

	public static void setTHRESHOLD(float tHRESHOLD) {
		THRESHOLD = tHRESHOLD;
	}

	public SequenceAlignmentTask doPerformParallel(String targetSequence, Matrix matrix, int startSeq,
												int endSeq, String databasePath, boolean firstTime){
		SequenceAlignmentTask result1;
		SequenceAlignmentTask result2;
		SequenceAlignmentTask result = null;

		if (firstTime == true) {
			result1 = this.doPerformParallel(targetSequence, matrix, startSeq, endSeq, databasePath, false);
			result2 = this.doPerformParallel(targetSequence, matrix, startSeq, endSeq, databasePath, false);
			result = result1;
			result = result2;
			return result;
		}

		// Si la cantidad de secuencias es mayor a un porcentaje del total
		// del tamaño de la base de datos, se comienza la comparación
		float percentage = getPercentage(startSeq, endSeq, databasePath);

		if (percentage < THRESHOLD) {
			result = getBestScore(targetSequence, matrix, startSeq, endSeq, databasePath);
			return result;
		}
		int halfSeq = (endSeq - startSeq)/2 + startSeq;
		int halfSeqPlusOne = halfSeq + 1;
		result1 = this.doPerformParallel(targetSequence, matrix, startSeq, halfSeq, databasePath, false);
		result2 = this.doPerformParallel(targetSequence, matrix, halfSeqPlusOne, endSeq, databasePath, false);
		result = getBestScore(result1, result2); 
		return result;
	}

	private float getPercentage(int startSeq, int endSeq, String databasePath) {
		try {
			DatabaseHandler instance = DatabaseHandler.instance(databasePath);
			return (float)(endSeq - startSeq) / instance.getSize();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
	}

	private SequenceAlignmentTask getBestScore(SequenceAlignmentTask a, SequenceAlignmentTask b) {
		if (a.getScore() > b.getScore())
			return a;
		return b;
	}

	private SequenceAlignmentTask getBestScore(String targetSequence, Matrix matrix, int startSeq, int endSeq, String databasePath) {
		try {
			Sequence target = SequenceParser.parse(targetSequence);
			DatabaseHandler instance = DatabaseHandler.instance(databasePath);
			Sequence seq = SequenceParser.parse(instance.getSequence(startSeq));
			int resultSeq = startSeq;
			Alignment best = SmithWatermanGotoh.align(seq, target, matrix, 10.0f, 0.5f);
			best.calculateScore();
			for (int i=startSeq + 1; i<=endSeq; i++) {
				seq = SequenceParser.parse(instance.getSequence(i));
				Alignment a = SmithWatermanGotoh.align(seq, target, matrix, 10.0f, 0.5f);
				a.calculateScore();
				if (a.getScore() > best.getScore()) {
					resultSeq = i;
					best = a;
				}
			}
			return new SequenceAlignmentTask(resultSeq, best.getScore());
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
	}

	/**
	 * Run the sample.
	 * @param args target sequence, database, threshold, parallelBoolean.
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception{
		try {
			String targetSequencePath = args[0];
			String databasePath = args[1];
			if (args.length > 2) {
				THRESHOLD = Float.parseFloat(args[2]);
			}			
			SequenceAlignmentRunner runner = new SequenceAlignmentRunner();
			System.out.println("Running: " + SequenceAlignmentRunner.class + " in parallel with threshold" + THRESHOLD);
			String s = FileUtils.readTextFile(targetSequencePath);
			int nb = DatabaseHandler.generateIndex(databasePath, System.getProperty("java.io.tmpdir") + File.separator + "foo.idx", null);
			long start = System.currentTimeMillis();
			Matrix matrix = MatrixLoader.load("PAM120");
			SequenceAlignmentTask task = null;

			task = runner.doPerformParallel(s, matrix, 1, nb, databasePath, true);

			long elapsed = System.currentTimeMillis() - start;
			System.out.println("Calculation time = " + elapsed);
			System.out.println("Max score is "+task.getScore()+" for sequence #"+task.getNumber());
		} catch (Exception e) {
			System.out.println("args usage: targetSeqPath databasePath [THRESHOLD]");
		}

	}


}
